/*******************************************************************************
 * Copyright (c) 2012-2017 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.plugin.languageserver.ide.quickopen;

import elemental.dom.Element;
import elemental.html.SpanElement;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.inject.Inject;

import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.autocomplete.AutoCompleteResources;
import org.eclipse.che.ide.ui.list.SimpleList;
import org.eclipse.che.ide.util.dom.Elements;
import org.eclipse.che.plugin.languageserver.ide.LanguageServerResources;
import org.eclipse.che.plugin.languageserver.ide.filters.Match;
import org.eclipse.che.plugin.languageserver.ide.quickopen.QuickOpenEntry.Mode;
import org.vectomatic.dom.svg.ui.SVGImage;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Evgen Vidolob
 * @author Dmitry Shnurenko
 */
public class QuickOpenViewImpl extends PopupPanel implements QuickOpenView {

    private final AutoCompleteResources.Css css;

    @UiField
    TextBox         nameField;

    @UiField
    DockLayoutPanel layoutPanel;

    @UiField
    FlowPanel       actionsPanel;

    @UiField
    HTML            actionsContainer;

    private ActionDelegate                  delegate;
    private Resources                       resources;
    private final LanguageServerResources   languageServerResources;
    private SimpleList<QuickOpenEntry>      list;

    private final SimpleList.ListItemRenderer<QuickOpenEntry> listItemRenderer =
        new SimpleList.ListItemRenderer<QuickOpenEntry>() {
            @Override
            public void render(Element itemElement, QuickOpenEntry itemData) {
                Element label = Elements.createSpanElement(css.proposalLabel());
                Element icon = Elements.createSpanElement(css.proposalIcon());
                Element group = Elements.createSpanElement(css.proposalGroup());

                SafeHtmlBuilder builder = new SafeHtmlBuilder();
                List<Match> highlights = itemData.getHighlights();
                String text = itemData.getLabel();
                int pos = 0;
                SpanElement spanElement = Elements.createSpanElement();
                for (Match highlight : highlights) {
                    if (highlight.getStart() == highlight.getEnd()) {
                        continue;
                    }

                    if (pos < highlight.getStart()) {
                        builder.appendHtmlConstant("<span>");
                        builder.appendEscaped(text.substring(pos, highlight.getStart()));
                        builder.appendHtmlConstant("</span>");
                    }

                    builder.appendHtmlConstant("<span class=\""+languageServerResources.quickOpenListCss().searchMatch()+"\">");
                    builder.appendEscaped(text.substring(highlight.getStart(), highlight.getEnd()));
                    builder.appendHtmlConstant("</span>");
                    pos = highlight.getEnd();
                }

                if (pos < text.length()) {
                    builder.appendHtmlConstant("<span>");
                    builder.appendEscaped(text.substring(pos));
                    builder.appendHtmlConstant("</span>");
                }
                spanElement.setInnerHTML(builder.toSafeHtml().asString());
                label.getStyle().setPaddingLeft("5px");
                label.getStyle().setPaddingRight("5px");
                if (itemData.getIcon() != null) {
                    SVGImage svgImage = new SVGImage(itemData.getIcon());
                    icon.appendChild((elemental.dom.Node)svgImage.getElement());
                    itemElement.appendChild(icon);
                }
                if(itemData instanceof QuickOpenEntryGroup) {
                    QuickOpenEntryGroup entryGroup = (QuickOpenEntryGroup)itemData;
                    if(entryGroup.isWithBorder()) {
                        Elements.addClassName(languageServerResources.quickOpenListCss().groupSeparator(), itemElement);
                    }
                    if (entryGroup.getGroupLabel() != null) {
                        group.setInnerText(entryGroup.getGroupLabel());
                    }
                } else {
                    if (itemData.getDescription() != null) {
                        group.setInnerText(itemData.getDescription());
                    }
                }

                label.appendChild(spanElement);
                itemElement.appendChild(label);
                itemElement.appendChild(group);
            }

            @Override
            public Element createElement() {
                return Elements.createDivElement();
            }
        };

    private QuickOpenModel model;

    private final SimpleList.ListEventDelegate<QuickOpenEntry> eventDelegate = new SimpleList.ListEventDelegate<QuickOpenEntry>() {
        @Override
        public void onListItemClicked(Element listItemBase, QuickOpenEntry itemData) {
            run(itemData, true);
        }

        @Override
        public void onListItemDoubleClicked(Element listItemBase, QuickOpenEntry itemData) {
        }
    };

    @Inject
    public QuickOpenViewImpl(Resources resources,
                             AutoCompleteResources autoCompleteResources,
                             QuickOpenViewImplUiBinder uiBinder,
                             LanguageServerResources languageServerResources) {
        this.resources = resources;
        this.languageServerResources = languageServerResources;

        css = autoCompleteResources.autocompleteComponentCss();
        css.ensureInjected();

        DockLayoutPanel rootElement = uiBinder.createAndBindUi(this);
        setWidget(rootElement);
        setAutoHideEnabled(true);
        setAnimationEnabled(true);

        layoutPanel.setWidgetHidden(actionsPanel, true);

        addCloseHandler(new CloseHandler<PopupPanel>() {
            @Override
            public void onClose(CloseEvent<PopupPanel> event) {
                delegate.onClose(event.isAutoClosed());
            }
        });
    }

    @Override
    public void setDelegate(ActionDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    public void focusOnInput() {
        nameField.setFocus(true);
    }

    @Override
    public void show(final String value) {
        super.show();

        nameField.setValue(value);

        setPopupPositionAndShow(new PositionCallback() {
            @Override
            public void setPosition(int offsetWidth, int offsetHeight) {
                setPopupPosition((Window.getClientWidth() / 2) - (offsetWidth/2), 60);
            }
        });

        Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
            @Override
            public void execute() {
                delegate.valueChanged(value);
                nameField.setFocus(true);
            }
        });
    }

    @Override
    public void setModel(QuickOpenModel model) {
        this.model = model;
        actionsContainer.getElement().setInnerHTML("");
        Element itemHolder = Elements.createDivElement();
        itemHolder.setClassName(css.items());
        actionsContainer.getElement().appendChild(((com.google.gwt.dom.client.Element) itemHolder));

        list = SimpleList.create((SimpleList.View)actionsContainer.getElement().cast(), (Element)actionsContainer.getElement(), itemHolder,
                                 languageServerResources.quickOpenListCss(), listItemRenderer, eventDelegate);

        list.render(new ArrayList<>(model.getEntries()));

        layoutPanel.setWidgetHidden(actionsPanel, false);
        layoutPanel.setHeight("200px");

        if(!nameField.getValue().isEmpty()){
            list.getSelectionModel().setSelectedItem(0);
            run(list.getSelectionModel().getSelectedItem(), false);
        }
    }

    protected void run(QuickOpenEntry entry, boolean isOpen) {
        if (entry == null) {
            return;
        }
        if (model.run(entry, isOpen ? Mode.OPEN : Mode.PREVIEW)) {
            hide(false);
        }
    }

    @UiHandler("nameField")
    void handleKeyDown(KeyDownEvent event) {
        switch (event.getNativeKeyCode()) {
            case KeyCodes.KEY_UP:
                event.stopPropagation();
                event.preventDefault();
                list.getSelectionModel().selectPrevious();
                run(list.getSelectionModel().getSelectedItem(), false);
                return;

            case KeyCodes.KEY_DOWN:
                event.stopPropagation();
                event.preventDefault();
                list.getSelectionModel().selectNext();
                run(list.getSelectionModel().getSelectedItem(), false);
                return;

            case KeyCodes.KEY_PAGEUP:
                event.stopPropagation();
                event.preventDefault();
                list.getSelectionModel().selectPreviousPage();
                run(list.getSelectionModel().getSelectedItem(), false);
                return;

            case KeyCodes.KEY_PAGEDOWN:
                event.stopPropagation();
                event.preventDefault();
                list.getSelectionModel().selectNextPage();
                run(list.getSelectionModel().getSelectedItem(), false);
                return;

            case KeyCodes.KEY_ENTER:
                event.stopPropagation();
                event.preventDefault();
                run(list.getSelectionModel().getSelectedItem(), true);
                return;

            case KeyCodes.KEY_ESCAPE:
                event.stopPropagation();
                event.preventDefault();
                hide(true);
                return;
        }

        Scheduler.get().scheduleDeferred(new Command() {
            @Override
            public void execute() {
                delegate.valueChanged(nameField.getText());
            }
        });
    }

    interface QuickOpenViewImplUiBinder extends UiBinder<DockLayoutPanel, QuickOpenViewImpl> {
    }

}
